<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'
import {
  getComplaintController,
  getHouseController,
  getOwnerController,
  getParkingChargeController,
  getParkingSpaceController,
  getPropertyFeeController,
  getRepairController,
  type ComplaintQueryRequest,
  type ComplaintVO,
  type HouseQueryRequest,
  type HouseVO,
  type OwnerVO,
  type OwnerQueryRequest,
  type ParkingChargeQueryRequest,
  type ParkingChargeVO,
  type ParkingSpaceQueryRequest,
  type ParkingSpaceVO,
  type PropertyFeeQueryRequest,
  type PropertyFeeVO,
  type RepairQueryRequest,
  type RepairVO,
} from '@/api-client'
import VueUiDonut from 'vue-data-ui/vue-ui-donut'
import VueUiKpi from 'vue-data-ui/vue-ui-kpi'
import VueUiXy from 'vue-data-ui/vue-ui-xy'
import type {
  VueUiDonutConfig,
  VueUiDonutDatasetItem,
  VueUiKpiConfig,
  VueUiXyConfig,
  VueUiXyDatasetItem,
} from 'vue-data-ui'

interface SummaryCard {
  id: string
  title: string
  value: number
  prefix?: string
  suffix?: string
  rounding?: number
  extra?: string
}

interface PaginationPayload {
  current?: number
  pageSize?: number
}

type PaginatedResponse<T> = {
  code?: number
  message?: string
  data?: {
    records?: T[]
    total?: number
    pages?: number
  }
}

const DEFAULT_PAGE_SIZE = 200
const DEFAULT_MAX_PAGES = 10
const DEFAULT_MAX_RECORDS = 2000

const PROPERTY_STATUS = [
  { value: 0, label: '未缴', color: '#f56c6c' },
  { value: 1, label: '已缴', color: '#67c23a' },
  { value: 2, label: '部分缴', color: '#e6a23c' },
] as const

const summaryCards = ref<SummaryCard[]>([
  { id: 'owners', title: '业主总数', value: 0, suffix: '户', rounding: 0 },
  { id: 'houses', title: '房屋总数', value: 0, suffix: '套', rounding: 0 },
  { id: 'houseRate', title: '房屋入住率', value: 0, suffix: '%', rounding: 1 },
  { id: 'parkingRate', title: '车位占用率', value: 0, suffix: '%', rounding: 1 },
  { id: 'complaintPending', title: '待处理投诉', value: 0, suffix: '件', rounding: 0 },
  { id: 'repairPending', title: '待处理报修', value: 0, suffix: '单', rounding: 0 },
  { id: 'propertyIncome', title: '累计物业收费', value: 0, prefix: '¥', rounding: 2 },
  { id: 'parkingIncome', title: '累计停车收费', value: 0, prefix: '¥', rounding: 2 },
])

const propertyStatusDataset = ref<VueUiDonutDatasetItem[]>([])
const parkingStatusDataset = ref<VueUiDonutDatasetItem[]>([])
const propertyStatusTotal = ref(0)
const parkingStatusTotal = ref(0)

const monthlyTrendDataset = ref<VueUiXyDatasetItem[]>([])
const monthlyLabels = ref<string[]>([])

const loading = ref(false)
const lastUpdatedAt = ref<string>('')

const ownerApi = getOwnerController()
const houseApi = getHouseController()
const parkingSpaceApi = getParkingSpaceController()
const propertyFeeApi = getPropertyFeeController()
const parkingChargeApi = getParkingChargeController()
const complaintApi = getComplaintController()
const repairApi = getRepairController()

function toFixedNumber(value: number, fractionDigits = 2) {
  return Number(value.toFixed(fractionDigits))
}

function getKpiConfig(card: SummaryCard): VueUiKpiConfig {
  return {
    title: card.title,
    prefix: card.prefix,
    suffix: card.suffix,
    valueRounding: card.rounding ?? 0,
    useAnimation: false,
  }
}

function setCardValue(id: string, value: number, extra?: string) {
  const target = summaryCards.value.find((card) => card.id === id)
  if (target) {
    target.value = value
    target.extra = extra
  }
}

async function fetchPaginated<T, P extends PaginationPayload>(
  fetcher: (payload: P) => Promise<PaginatedResponse<T>>,
  basePayload: P,
  options?: { pageSize?: number; maxPages?: number; maxRecords?: number },
): Promise<{ records: T[]; total: number }> {
  const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE
  const maxPages = options?.maxPages ?? DEFAULT_MAX_PAGES
  const maxRecords = options?.maxRecords ?? DEFAULT_MAX_RECORDS

  let current = 1
  let total = 0
  const collected: T[] = []

  while (current <= maxPages && collected.length < maxRecords) {
    const payload = { ...basePayload, current, pageSize } as P
    const response = (await fetcher(payload)) as PaginatedResponse<T>

    if (response.code !== 200 || !response.data) {
      throw new Error(response.message || '请求失败')
    }

    const pageRecords = Array.isArray(response.data.records) ? response.data.records : []
    collected.push(...pageRecords)
    total = response.data.total ?? total

    const expectedTotal = response.data.total ?? collected.length
    if (pageRecords.length < pageSize || collected.length >= expectedTotal) {
      break
    }

    current += 1
  }

  return { records: collected, total: total || collected.length }
}

function aggregateByMonth<T>(
  items: T[],
  dateGetter: (item: T) => string | undefined,
  valueGetter: (item: T) => number,
) {
  const result = new Map<string, number>()
  items.forEach((item) => {
    const rawDate = dateGetter(item)
    if (!rawDate) return
    const parsed = dayjs(rawDate)
    if (!parsed.isValid()) return
    const key = parsed.format('YYYY-MM')
    const currentValue = result.get(key) ?? 0
    result.set(key, currentValue + valueGetter(item))
  })
  return result
}

const propertyStatusConfig = computed<VueUiDonutConfig>(() => ({
  responsive: true,
  style: {
    chart: {
      title: { text: '物业收费状态分布', fontSize: 16, bold: true },
      legend: {
        show: true,
        showValue: true,
        showPercentage: true,
        roundingValue: 0,
        roundingPercentage: 1,
      },
      layout: {
        labels: {
          dataLabels: { show: true },
          value: { rounding: 0, prefix: '¥' },
          percentage: { show: true, rounding: 1 },
          hollow: {
            show: true,
            total: {
              show: true,
              text: '累计金额',
              value: { prefix: '¥', rounding: 0 },
            },
          },
        },
      },
    },
  },
}))

const parkingStatusConfig = computed<VueUiDonutConfig>(() => ({
  responsive: true,
  style: {
    chart: {
      title: { text: '停车收费状态分布', fontSize: 16, bold: true },
      legend: {
        show: true,
        showValue: true,
        showPercentage: true,
        roundingValue: 0,
        roundingPercentage: 1,
      },
      layout: {
        labels: {
          dataLabels: { show: true },
          value: { rounding: 0, prefix: '¥' },
          percentage: { show: true, rounding: 1 },
          hollow: {
            show: true,
            total: {
              show: true,
              text: '累计金额',
              value: { prefix: '¥', rounding: 0 },
            },
          },
        },
      },
    },
  },
}))

const trendConfig = computed<VueUiXyConfig>(() => ({
  responsive: true,
  chart: {
    title: { text: '近6个月收费趋势', fontSize: 16, bold: true },
    legend: { show: true },
    grid: {
      labels: {
        xAxisLabels: {
          show: true,
          values: monthlyLabels.value,
          rotation: monthlyLabels.value.length > 6 ? 45 : 0,
        },
        yAxis: {
          showBaseline: true,
        },
      },
    },
  },
}))

const hasTrendData = computed(() =>
  monthlyTrendDataset.value.some((serie) =>
    serie.series.some((value) => typeof value === 'number' && value > 0),
  ) && monthlyLabels.value.length > 0,
)

const hasPropertyStatus = computed(() =>
  propertyStatusDataset.value.some((item) => item.values.some((val) => (val ?? 0) > 0)),
)

const hasParkingStatus = computed(() =>
  parkingStatusDataset.value.some((item) => item.values.some((val) => (val ?? 0) > 0)),
)

const refreshing = computed(() => loading.value)

const handleRefresh = () => {
  fetchStatistics()
}

async function fetchStatistics() {
  loading.value = true
  try {
    const [
      ownerResult,
      houseResult,
      parkingSpaceResult,
      propertyResult,
      parkingChargeResult,
      complaintResult,
      repairResult,
    ] = await Promise.all([
      fetchPaginated<OwnerVO, OwnerQueryRequest>(ownerApi.listOwner, {} as OwnerQueryRequest, {
        pageSize: 1,
        maxPages: 1,
      }),
      fetchPaginated<HouseVO, HouseQueryRequest>(houseApi.queryHouseByPage, {} as HouseQueryRequest, {
        pageSize: 200,
      }),
      fetchPaginated<ParkingSpaceVO, ParkingSpaceQueryRequest>(
        parkingSpaceApi.search2,
        {} as ParkingSpaceQueryRequest,
        {
          pageSize: 200,
        },
      ),
      fetchPaginated<PropertyFeeVO, PropertyFeeQueryRequest>(
        propertyFeeApi.search1,
        {} as PropertyFeeQueryRequest,
        {
          pageSize: 200,
        },
      ),
      fetchPaginated<ParkingChargeVO, ParkingChargeQueryRequest>(
        parkingChargeApi.search3,
        {} as ParkingChargeQueryRequest,
        {
          pageSize: 200,
        },
      ),
      fetchPaginated<ComplaintVO, ComplaintQueryRequest>(
        complaintApi.search5,
        {} as ComplaintQueryRequest,
        {
          pageSize: 200,
        },
      ),
      fetchPaginated<RepairVO, RepairQueryRequest>(repairApi.search, {} as RepairQueryRequest, {
        pageSize: 200,
      }),
    ])

    const ownerTotal = ownerResult.total
    const houseTotal = houseResult.total
    const houseOccupied = houseResult.records.filter((item) => item.status === 1).length
    const parkingSpaceTotal = parkingSpaceResult.total
    const parkingSpaceOccupied = parkingSpaceResult.records.filter((item) => item.status === 1).length

    const propertyTotalAmount = propertyResult.records.reduce(
      (sum, item) => sum + (item.money ?? 0),
      0,
    )
    const parkingTotalAmount = parkingChargeResult.records.reduce(
      (sum, item) => sum + (item.money ?? 0),
      0,
    )

    const complaintPending = complaintResult.records.filter((item) => item.status !== 1).length
    const repairPending = repairResult.records.filter((item) => (item.status ?? 0) < 2).length

    setCardValue('owners', ownerTotal, `共 ${ownerTotal} 名业主`)
    setCardValue('houses', houseTotal, `入住 ${houseOccupied} 套`)

    const houseRate = houseTotal > 0 ? (houseOccupied / houseTotal) * 100 : 0
    setCardValue(
      'houseRate',
      toFixedNumber(houseRate, 1),
      houseTotal > 0 ? `${houseOccupied}/${houseTotal} 入住` : '暂无房屋数据',
    )

    const parkingRate = parkingSpaceTotal > 0 ? (parkingSpaceOccupied / parkingSpaceTotal) * 100 : 0
    setCardValue(
      'parkingRate',
      toFixedNumber(parkingRate, 1),
      parkingSpaceTotal > 0
        ? `${parkingSpaceOccupied}/${parkingSpaceTotal} 已占用`
        : '暂无车位数据',
    )

    setCardValue('complaintPending', complaintPending, `总计 ${complaintResult.total} 件`)
    setCardValue('repairPending', repairPending, `总计 ${repairResult.total} 单`)

    setCardValue('propertyIncome', toFixedNumber(propertyTotalAmount, 2), `共 ${propertyResult.total} 笔`)
    setCardValue('parkingIncome', toFixedNumber(parkingTotalAmount, 2), `共 ${parkingChargeResult.total} 笔`)

    const propertyStatusAmount = new Map<number, number>()
    propertyResult.records.forEach((item) => {
      const status = typeof item.status === 'number' ? item.status : 0
      const current = propertyStatusAmount.get(status) ?? 0
      propertyStatusAmount.set(status, current + (item.money ?? 0))
    })
    propertyStatusDataset.value = PROPERTY_STATUS.map((status) => ({
      name: status.label,
      color: status.color,
      values: [toFixedNumber(propertyStatusAmount.get(status.value) ?? 0, 2)],
    }))
    propertyStatusTotal.value = toFixedNumber(propertyTotalAmount, 2)

    const parkingStatusAmount = new Map<number, number>()
    parkingChargeResult.records.forEach((item) => {
      const status = typeof item.status === 'number' ? item.status : 0
      const current = parkingStatusAmount.get(status) ?? 0
      parkingStatusAmount.set(status, current + (item.money ?? 0))
    })
    parkingStatusDataset.value = PROPERTY_STATUS.map((status) => ({
      name: status.label,
      color: status.color,
      values: [toFixedNumber(parkingStatusAmount.get(status.value) ?? 0, 2)],
    }))
    parkingStatusTotal.value = toFixedNumber(parkingTotalAmount, 2)

    const propertyMonthly = aggregateByMonth<PropertyFeeVO>(
      propertyResult.records,
      (item) => item.startDate || item.endDate,
      (item) => item.money ?? 0,
    )
    const parkingMonthly = aggregateByMonth<ParkingChargeVO>(
      parkingChargeResult.records,
      (item) => item.payDate || item.endDate,
      (item) => item.money ?? 0,
    )
    const allMonths = Array.from(new Set([...propertyMonthly.keys(), ...parkingMonthly.keys()]))
    const sortedMonths = allMonths
      .filter((month) => dayjs(month, 'YYYY-MM', true).isValid())
      .sort((a, b) => dayjs(a).valueOf() - dayjs(b).valueOf())
    const monthsToUse = sortedMonths.slice(-6)
    monthlyLabels.value = monthsToUse
    monthlyTrendDataset.value = [
      {
        name: '物业收费',
        type: 'line',
        smooth: true,
        series: monthsToUse.map((month) => toFixedNumber(propertyMonthly.get(month) ?? 0, 2)),
      },
      {
        name: '停车收费',
        type: 'bar',
        series: monthsToUse.map((month) => toFixedNumber(parkingMonthly.get(month) ?? 0, 2)),
      },
    ]

    lastUpdatedAt.value = dayjs().format('YYYY-MM-DD HH:mm')
  } catch (error) {
    console.error('fetchStatistics error:', error)
    ElMessage.error('获取统计数据失败，请稍后重试')
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  fetchStatistics()
})
</script>

<template>
  <div class="statistical-analysis-view" v-loading="refreshing">
    <div class="header">
      <div class="header-title">数据统计</div>
      <div class="header-actions">
        <el-button type="primary" plain :loading="loading" @click="handleRefresh">
          刷新数据
        </el-button>
        <span class="header-updated" v-if="lastUpdatedAt">上次更新：{{ lastUpdatedAt }}</span>
      </div>
    </div>

    <el-row :gutter="16" class="summary-row">
      <el-col
        v-for="card in summaryCards"
        :key="card.id"
        :xs="24"
        :sm="12"
        :md="8"
        :lg="6"
      >
        <el-card shadow="hover" class="summary-card">
          <VueUiKpi :dataset="card.value" :config="getKpiConfig(card)" />
          <div v-if="card.extra" class="summary-extra">{{ card.extra }}</div>
        </el-card>
      </el-col>
    </el-row>

    <el-row :gutter="16" class="charts-row">
      <el-col :xs="24" :md="12">
        <el-card shadow="hover" class="chart-card">
          <template #header>
            <div class="card-header">
              <span>物业收费占比</span>
              <span class="chart-total">累计金额：¥{{ propertyStatusTotal }}</span>
            </div>
          </template>
          <VueUiDonut
            v-if="hasPropertyStatus"
            :dataset="propertyStatusDataset"
            :config="propertyStatusConfig"
          />
          <el-empty v-else description="暂无收费数据" />
        </el-card>
      </el-col>
      <el-col :xs="24" :md="12">
        <el-card shadow="hover" class="chart-card">
          <template #header>
            <div class="card-header">
              <span>停车收费占比</span>
              <span class="chart-total">累计金额：¥{{ parkingStatusTotal }}</span>
            </div>
          </template>
          <VueUiDonut
            v-if="hasParkingStatus"
            :dataset="parkingStatusDataset"
            :config="parkingStatusConfig"
          />
          <el-empty v-else description="暂无收费数据" />
        </el-card>
      </el-col>
    </el-row>

    <el-card shadow="hover" class="trend-card">
      <template #header>
        <div class="card-header">
          <span>收费趋势</span>
          <span v-if="monthlyLabels.length" class="chart-total">
            时间范围：{{ monthlyLabels[0] }} ~ {{ monthlyLabels[monthlyLabels.length - 1] }}
          </span>
        </div>
      </template>
      <VueUiXy v-if="hasTrendData" :dataset="monthlyTrendDataset" :config="trendConfig" />
      <el-empty v-else description="暂无趋势数据" />
    </el-card>
  </div>
</template>

<style scoped>
.statistical-analysis-view {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background: #fff;
  border-radius: 8px;
  border: 1px solid #ebeef5;
}

.header-title {
  font-size: 20px;
  font-weight: 600;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: #909399;
}

.header-updated {
  white-space: nowrap;
}

.summary-row {
  margin-top: 0;
}

.summary-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 140px;
}

.summary-extra {
  font-size: 13px;
  color: #909399;
}

.charts-row {
  margin-top: 0;
}

.chart-card {
  min-height: 340px;
}

.trend-card {
  min-height: 420px;
}

.card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: 16px;
  font-weight: 500;
}

.chart-total {
  font-size: 13px;
  color: #909399;
}

@media (max-width: 768px) {
  .card-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
}
</style>
